#include <netinet/ip.h>
#include <netinet/udp.h>
#include <time.h>
#include <string.h>


#include "multicast_routing_table.h"

#include "timer_queue.h"
#include "bloom_filter.h"
#include "util.h"
#include "typecast_forwarding.h"
#include "type_routingtable.h"
#include "routing_table.h"

#define MAX_NEIGHBOR_NUMBER 16 

struct ip_mac_entry { 
    u_int32_t ip; 
    char mac_addr[8]; 
    unsigned char len;  
}; 

static struct ip_mac_entry neighbor_mac[MAX_NEIGHBOR_NUMBER]; 
static int neighbor_number = 0; 

static u_int32_t lookup_neighbor_by_mac(unsigned char mac_addr[], unsigned char mac_len);

void typecast_update_neighbor_mac(u_int32_t neighbor_ip, unsigned char mac_addr[], unsigned char mac_len) 
{ 
    int i; 
    if (mac_len <= 0) { 
        return; 
    }
     for(i=0; i<neighbor_number; i++) { 
        if ( (neighbor_mac[i].len == mac_len) && 
             (memcmp(neighbor_mac[i].mac_addr, mac_addr, mac_len) == 0)) {
            neighbor_mac[i].ip = neighbor_ip; 
            return; 
        }
    }
    
    if (neighbor_number >= MAX_NEIGHBOR_NUMBER) { 
        printf("Maximal neighbor number reached\n"); 
        exit(1); 
    }
    
    neighbor_mac[neighbor_number].ip = neighbor_ip; 
    neighbor_mac[neighbor_number].len = mac_len; 
    memcpy(neighbor_mac[neighbor_number].mac_addr, mac_addr, mac_len);
    neighbor_number++;   
    printf("set mac address of %s to %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n", 
       ip_to_string(neighbor_ip), mac_addr[0],mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);                           	
}

static 
u_int32_t lookup_neighbor_by_mac(unsigned char mac_addr[], unsigned char mac_len)
{
    int i; 
    for(i=0; i<neighbor_number; i++) { 
        if ( (neighbor_mac[i].len == mac_len) && 
             (memcmp(neighbor_mac[i].mac_addr, mac_addr, mac_len) == 0)) {
                return neighbor_mac[i].ip; 
             }
    }
    return 0;                     
}

/**
 * Check whether should forward an IP packet based on TypeCast
 * routing table. 
 * Return 1 if the packet should be forwarded to next hops. 
 * 0 otherwise. 
 * 
 */

 
int typecast_forward_packet(struct iphdr * ip, unsigned char mac_addr[], unsigned char mac_addr_len) 
{
    u_int32_t src_addr = ntohl(ip->saddr); 
    u_int32_t dst_addr = ntohl(ip->daddr); 
    u_int32_t prev_hop = 0; 
    rt_table_t * rev_rt = NULL; 
    
    mrt_table_t * grp_rt = mrt_table_find(dst_addr); 
    if (grp_rt == NULL) { 
        return 0; 
    }
    
    prev_hop = lookup_neighbor_by_mac(mac_addr,mac_addr_len); 
#ifdef DEBUG_TYPECAST
    printf("Previous hop ip address for group %s node %s is %s\n", ip_to_string(dst_addr), ip_to_string(src_addr), ip_to_string(prev_hop));     
#endif
    
    const char * target_filter = get_target_bloom_filter(ip); 
    time_t now = time(NULL); 

    multicast_neighbor_node* typecast_neighbor;     
    mrt_nexthop_t *nexthop = grp_rt->nexthops; 
    while(nexthop != NULL) { 
        if (nexthop->addr != prev_hop && nexthop->activated) { 
            typecast_neighbor = FindMulticastNeighbor(dst_addr, nexthop->addr);                 
            if (typecast_neighbor != NULL) { 
                if (typecast_neighbor->expire >= now) { 
                    if(is_subfilter(typecast_neighbor->FBF, target_filter)) { 
                        return 1; 
                    }
                } else { 
                    printf("!!! Typecast routing entry found for group %s node %s is expired, accept packet\n", ip_to_string(dst_addr), ip_to_string(nexthop->addr));                     
                    return 1; 
                }
            } else { 
                printf("!!! Typecast no routing entry found for group %s node %s, accept packet\n", ip_to_string(dst_addr), ip_to_string(nexthop->addr)); 
                return 1; 
            }
        }
        nexthop = nexthop->next; 
    }
    return 0; 
    /*    
    multicast_group_node * grp_entry = FindMulticastGroup(dst_addr);     
    if (grp_entry == NULL) { 
        return 0; 
    }
    char * target_filter = get_target_bloom_filter(ip); 
    multicast_neighbor_node * neighbor = grp_entry->neighborHead; 

    time_t now = time(NULL); 
    
    while (neighbor != NULL) { 
        if ((neighbor->multicast_neighbor_addr != prev_hop) && (now <= neighbor->expire)) {   
           if (is_subfilter(neighbor->FBF, target_filter)) { 
                return 1; 
           }
        }
        neighbor = neighbor->next; 
    }              
    return 0;     
    */
}

int typecast_local_delivery(struct iphdr * ip) 
{
    u_int32_t src_addr = ntohl(ip->saddr); 
    u_int32_t dst_addr = ntohl(ip->daddr); 
    multicast_group_node * grp_entry = FindMulticastGroup(dst_addr);     
    if (grp_entry == NULL) { 
        return 0; 
    }
    
    if (is_subfilter(grp_entry->LBF, get_target_bloom_filter(ip))) { 
        return 1; 
    }
    return 0; 
}

const char * get_target_bloom_filter(struct iphdr * ip)
{
    struct udphdr * udp = (struct udphdr *) ((char *) ip + (ip->ihl << 2));
    const char * filter = (char *)udp + sizeof(struct udphdr); 
    return filter;     
}

